package link.wangkui;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.io.IoUtil;
import cn.hutool.poi.excel.ExcelUtil;
import cn.hutool.poi.excel.ExcelWriter;
import cn.hutool.poi.excel.StyleSet;
import org.apache.poi.ss.usermodel.*;

import java.io.IOException;
import java.lang.reflect.Field;
import java.nio.charset.StandardCharsets;
import java.util.*;

/**
 * @Author: wqf
 * @Date: 2021/05/28
 * @Description: hutool 工具导出excel(非填充模板，手工画)
 */
public class HutoolExcelUtil {
    /**
     * YYYY/MM/dd 时间格式
     */
    private static final short LOCAL_DATE_FORMAT_SLASH = 14;

    /**
     * 方法描述: 创建excel
     *
     * @param isXlsx excel文件类型 true-xlsx/false-xls
     * @return cn.hutool.poi.excel.ExcelWriter
     * @author wqf
     * @date 2021/6/1 9:47
     */
    public static ExcelWriter createExcel(boolean isXlsx) {
        return cn.hutool.poi.excel.ExcelUtil.getWriter(isXlsx);
    }

    /**
     * 方法描述: 全局基础样式设置
     * 默认 全局水平居中+垂直居中
     * 默认 自动换行
     * 默认单元格边框颜色为黑色，细线条
     * 默认背景颜色为白色
     *
     * @param writer writer
     * @param font   字体样式
     * @return cn.hutool.poi.excel.StyleSet
     * @author wqf
     * @date 2021/5/28 10:43
     */
    public static StyleSet setBaseGlobalStyle(ExcelWriter writer, Font font) {
        //全局样式设置
        StyleSet styleSet = writer.getStyleSet();
        //设置全局文本居中
        styleSet.setAlign(HorizontalAlignment.CENTER, VerticalAlignment.CENTER);
        //设置全局字体样式
        styleSet.setFont(font, true);
        //设置背景颜色 第二个参数表示是否将样式应用到头部
        styleSet.setBackgroundColor(IndexedColors.WHITE, true);
        //设置自动换行 当文本长于单元格宽度是否换行
        //styleSet.setWrapText();
        // 设置全局边框样式
        styleSet.setBorder(BorderStyle.THIN, IndexedColors.BLACK);
        return styleSet;
    }

    /**
     * 方法描述: 设置标题的基础样式
     *
     * @param styleSet            StyleSet
     * @param font                字体样式
     * @param horizontalAlignment 水平排列方式
     * @param verticalAlignment   垂直排列方式
     * @return org.apache.poi.ss.usermodel.CellStyle
     * @author wqf
     * @date 2021/5/28 10:16
     */
    public static CellStyle createHeadCellStyle(StyleSet styleSet, Font font,
                                                HorizontalAlignment horizontalAlignment,
                                                VerticalAlignment verticalAlignment) {
        CellStyle headCellStyle = styleSet.getHeadCellStyle();
        headCellStyle.setAlignment(horizontalAlignment);
        headCellStyle.setVerticalAlignment(verticalAlignment);
        headCellStyle.setFont(font);
        return headCellStyle;
    }

    /**
     * 方法描述: 设置基础字体样式字体 这里保留最基础的样式使用
     *
     * @param bold     是否粗体
     * @param fontName 字体名称
     * @param fontSize 字体大小
     * @return org.apache.poi.ss.usermodel.Font
     * @author wqf
     * @date 2021/5/19 15:58
     */
    public static Font createFont(ExcelWriter writer, boolean bold, boolean italic, String fontName, int fontSize) {
        Font font = writer.getWorkbook().createFont();
        //设置字体名称 宋体 / 微软雅黑 /等
        font.setFontName(fontName);
        //设置是否斜体
        font.setItalic(italic);
        //设置字体大小 以磅为单位
        font.setFontHeightInPoints((short) fontSize);
        //设置是否加粗
        font.setBold(bold);
        return font;
    }

    /**
     * 方法描述: 设置行或单元格基本样式
     *
     * @param writer              writer
     * @param font                字体样式
     * @param verticalAlignment   垂直居中
     * @param horizontalAlignment 水平居中
     * @return void
     * @author wqf
     * @date 2021/5/28 10:28
     */
    public static CellStyle createCellStyle(ExcelWriter writer, Font font, boolean wrapText,
                                            VerticalAlignment verticalAlignment,
                                            HorizontalAlignment horizontalAlignment) {
        CellStyle cellStyle = writer.getWorkbook().createCellStyle();
        cellStyle.setVerticalAlignment(verticalAlignment);
        cellStyle.setAlignment(horizontalAlignment);
        cellStyle.setWrapText(wrapText);
        cellStyle.setFont(font);
        return cellStyle;
    }

    /**
     * 方法描述: 设置边框样式
     *
     * @param cellStyle 样式对象
     * @param bottom    下边框
     * @param left      左边框
     * @param right     右边框
     * @param top       上边框
     * @return void
     * @author wqf
     * @date 2021/5/28 10:23
     */
    public static void setBorderStyle(CellStyle cellStyle, BorderStyle bottom, BorderStyle left, BorderStyle right,
                                      BorderStyle top) {
        cellStyle.setBorderBottom(bottom);
        cellStyle.setBorderLeft(left);
        cellStyle.setBorderRight(right);
        cellStyle.setBorderTop(top);
    }

    /**
     * 方法描述: 获取对象需要导出的列和别名 这里按字段顺序来（可以在自定义注解上添加属性标识字段顺序,重写方法）
     * 在需要导出的字段上贴上注解 这里是@ExcelProperty，也可以用自定义注解
     * 注解需要有value 标识别名 order 标识字段顺序
     *
     * @param clazz 对象类型
     * @return int 导出的字段个数
     * @author wqf
     * @date 2021/4/29 16:56
     */
//    public static int setHeaderAlias(ExcelWriter writer, Class<?> clazz) {
//        //需要导出的字段数
//        Field[] fields = clazz.getDeclaredFields();
//        TreeMap<Integer, Map<String, String>> headerAliasMap = new TreeMap<>();
//        Arrays.stream(fields).forEach(f -> {
//            if (f.isAnnotationPresent(ExcelProperty.class)) {
//                ExcelProperty annotation = f.getAnnotation(ExcelProperty.class);
//                //别名
//                String[] value = annotation.value();
//                int order = annotation.order();
//                Map<String, String> alisMap = new HashMap<>();
//                String fieldName = f.getName();
//                //字段名
//                String headerAlias = value[0];
//                alisMap.put(fieldName, headerAlias);
//                headerAliasMap.put(order, alisMap);
//            }
//        });
//        LinkedHashMap<String, String> linkedMap = new LinkedHashMap<>();
//        for (Map.Entry<Integer, Map<String, String>> entry : headerAliasMap.entrySet()) {
//            linkedMap.putAll(entry.getValue());
//        }
//        writer.setHeaderAlias(linkedMap);
//        return linkedMap.size();
//    }

    /**
     * 方法描述: 自适应宽度(中文支持)
     *
     * @param sheet 页
     * @param size  因为for循环从0开始，size值为 列数-1
     * @return void
     * @author wqf
     * @date 2021/5/28 14:06
     */
    public static void setSizeColumn(Sheet sheet, int size) {
        for (int columnNum = 0; columnNum <= size; columnNum++) {
            int columnWidth = sheet.getColumnWidth(columnNum) / 256;
            for (int rowNum = 0; rowNum <= sheet.getLastRowNum(); rowNum++) {
                Row currentRow;
                //当前行未被使用过
                if (sheet.getRow(rowNum) == null) {
                    currentRow = sheet.createRow(rowNum);
                } else {
                    currentRow = sheet.getRow(rowNum);
                }

                if (currentRow.getCell(columnNum) != null) {
                    Cell currentCell = currentRow.getCell(columnNum);
                    if (currentCell.getStringCellValue() == CellType.STRING.name()) {
                        int length = currentCell.getStringCellValue().getBytes().length;
                        if (columnWidth < length) {
                            columnWidth = length;
                        }
                    }
                }
            }
            sheet.setColumnWidth(columnNum, columnWidth * 256);
        }
    }

    /**
     * 方法描述: excel 导出下载
     *
     * @param response 响应
     * @param fileName 文件名
     * @param writer   writer
     * @return void
     * @author wqf
     * @date 2021/6/1 11:11
     */
//    public static void downloadExcel(HttpServletResponse response, String fileName, ExcelWriter writer) {
//        response.setContentType("application/vnd.ms-excel;charset=utf-8");
//        // test.xls是弹出下载对话框的文件名，不能为中文，中文请自行编码
//        ServletOutputStream out = null;
//        try {
//            // 设置请求头属性
//            response.setHeader("Content-Disposition",
//                    "attachment;filename=" + new String((fileName + ".xlsx").getBytes(), StandardCharsets.ISO_8859_1));
//            out = response.getOutputStream();
//            // 写出到文件
//            writer.flush(out, true);
//            // 关闭writer，释放内存
//            writer.close();
//            // 此处记得关闭输出Servlet流
//            IoUtil.close(out);
//        } catch (IOException e) {
//            log.info("文件下载失败==" + e);
//        }
//    }

    /**
     * 方法描述: 最简单的导出,直接将数据放到excel
     * 导入的数据最好是List<map>格式数据,主要原因是List<Object> ,
     * 对象有字段是LocalDate或者是LocalDateTime类型的数据，
     * 时间格式化可能达不到想要的效果
     *
     * @param data       对象集合 两种
     * @param globalFont 全局字体样式
     * @param clazz      导出的对应class类型
     * @return void
     * @author wqf
     * @date 2021/5/28 11:22
     */
//    public static ExcelWriter exportBaseExcel(ExcelWriter writer, List<?> data, Font globalFont, Class clazz) {
//        Sheet sheet = writer.getSheet();
//        writer.setStyleSet(setBaseGlobalStyle(writer, globalFont));
//        int columnSize = setHeaderAlias(writer, clazz);
//        writer.setOnlyAlias(true);
//        writer.write(data);
//        setSizeColumn(sheet, columnSize);
//        return writer;
//    }
    public static Font setBaseFont(Font font, boolean bold, boolean italic, String fontName, int fontSize) {
        //设置字体名称 宋体 / 微软雅黑 /等
        font.setFontName(fontName);
        //设置是否斜体
        font.setItalic(italic);
        //设置字体高度
        //font.setFontHeight((short) fontHeight);
        //设置字体大小 以磅为单位
        font.setFontHeightInPoints((short) fontSize);
        //设置是否加粗
        font.setBold(bold);
        //默认字体颜色
        // font.setColor(Font.COLOR_NORMAL);
        //红色
        //font.setColor(Font.COLOR_RED);
        //设置下划线样式
        //font.setUnderline(Font.ANSI_CHARSET);
        //设定文字删除线
        //font.setStrikeout(true);
        return font;
    }

    /**
     * 输入标题到excel
     *
     * @param writer    excel对象
     * @param column    当前列位置
     * @param cellValue 标题内容
     */
    public static void writeCell(ExcelWriter writer, int column, String cellValue, Boolean required) {
        // 根据x,y轴设置单元格内容
        writer.writeCellValue(column, 0, cellValue);

        Font font = writer.createFont();
        // 根据x,y轴获取当前单元格样式
        CellStyle cellStyle = writer.createCellStyle(column, 0);
        // 内容水平居中
        cellStyle.setAlignment(HorizontalAlignment.CENTER);
        // 内容垂直居中
        cellStyle.setVerticalAlignment(VerticalAlignment.CENTER);
        // 设置边框
        cellStyle.setBorderBottom(BorderStyle.THIN);
        cellStyle.setBorderLeft(BorderStyle.THIN);
        cellStyle.setBorderRight(BorderStyle.THIN);
        if (required) {
            // 字体颜色标红
            short index = IndexedColors.LIGHT_YELLOW.index;
            cellStyle.setFillForegroundColor(index);
            cellStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND);
        }
        cellStyle.setFont((setBaseFont(font, true, false, "宋体", 12)));
    }

    public static void main(String[] args) {
        List<String> row1 = CollUtil.newArrayList("aa", "bb", "cc", "dd");
        List<String> row2 = CollUtil.newArrayList("aa1", "bb1", "cc1", "dd1");
        List<String> row3 = CollUtil.newArrayList("aa2", "bb2", "cc2", "dd2");
        List<String> row4 = CollUtil.newArrayList("aa3", "bb3", "cc3", "dd3");
        List<String> row5 = CollUtil.newArrayList("aa4", "bb4", "cc4", "dd4");

        List<List<String>> rows = CollUtil.newArrayList(row1, row2, row3, row4, row5);
        ExcelWriter writer = ExcelUtil.getWriter("d:/writeTest.xlsx");
        //通过构造方法创建writer
        //ExcelWriter writer = new ExcelWriter("d:/writeTest.xls");
        // 定义单元格背景色
        StyleSet style = writer.getStyleSet();
        // 第二个参数表示是否也设置头部单元格背景
        style.setBackgroundColor(IndexedColors.RED, false);
        //跳过当前行，既第一行，非必须，在此演示用
        writer.passCurrentRow();

        //合并单元格后的标题行，使用默认标题样式
        writer.merge(row1.size() - 1, "测试标题");
        //一次性写出内容，强制输出标题
        writer.write(rows, true);
        style.setBackgroundColor(IndexedColors.WHITE, false);
        writer.setStyleSet(setBaseGlobalStyle(writer,createFont(writer,false,false,"宋体",10)));
        writer.writeRow(row5);
        //关闭writer，释放内存
        writer.close();
        System.out.println("Hello World!");
    }
}
